﻿using System;
using System.Web;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Test")]
namespace ProjetoComperio.DataLayer.Infra
{
    public class ComperioDataObject
    {

        #region Privates

        /// <summary>

        /// Dictionary key for the DataContext in HttpContext.Current.Items

        /// </summary>
        private const string DATACONTEXT_ITEMS_KEY = "ComperioDataContextKey";

        [ThreadStatic]
        private static IComperioEntitiesDataModel _context;

        /// <summary>

        /// Private property to store the DataContext in the HttpContext.Current.Items or thread local storage

        /// </summary>
        private static IComperioEntitiesDataModel InternalDataContext
        {

            get
            {

                if (HttpContext.Current == null)

                    return _context;

                else

                    return (IComperioEntitiesDataModel)HttpContext.Current.Items[DATACONTEXT_ITEMS_KEY];

            }
            set
            {

                if (HttpContext.Current == null)

                    _context = value;

                else

                    HttpContext.Current.Items[DATACONTEXT_ITEMS_KEY] = value;

            }

        }

        #endregion

        #region Public and Protected Properties and Methods

        /// <summary>
        /// Returns the current DataContext. If none configured yet, then creates a new one and returns it. Internal access
        /// so that only the DAL layer can access.
        /// </summary>
        /// <returns>A reference to a DataContext</returns>
        protected IComperioEntitiesDataModel DbContext
        {

            get
            {
                // If the context is missing, create a new one
                if (InternalDataContext == null || !InternalDataContext.IsInitialized)
                {
                    // Note: in a real app, this should get the connection string from secure storage and pass it to the context constructor.
                    // However, it doesn't work because you cannot pass in parameters to new(). It's messy, but you can use reflection to solve this.
                    InternalDataContext = DataLayer.Infra.DataContextFactory.FactoryDataContext();
                    InternalDataContext.IsInitialized = true;

                }
                return InternalDataContext;

            }

        }

        /// <summary>

        /// Cleanup the context (dispose the context and set it to null). Public scope allows calling from upper tiers.

        /// </summary>
        public static void CleanUp()
        {

            if (InternalDataContext != null)
            {

                InternalDataContext.Dispose();
                GC.SuppressFinalize(InternalDataContext);
            }

        }

        /// <summary>
        /// This constructor creates the default instance that should be running
        /// </summary>
        public ComperioDataObject()
        {
            _context = DataContextFactory.FactoryDataContext();
        }

        /// <summary>
        /// This method should be used when another context is in use, such as FakeContext to test
        /// </summary>
        /// <param name="comperioEntitiesDataModel"></param>
        public void InitializeDataContext(IComperioEntitiesDataModel comperioEntitiesDataModel)
        {
            _context = comperioEntitiesDataModel;
            InternalDataContext = comperioEntitiesDataModel;
        }

        #endregion

    }
}
